package com.corecloud.config.mobile;

import org.springframework.security.authentication.AuthenticationServiceException;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
import org.springframework.util.Assert;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

/**
 * 该类对应普通登陆的 UsernamePasswordAuthenticationFilter
 */
public class SmsCodeAuthenticationFilter  extends AbstractAuthenticationProcessingFilter {
    public static final String SPRING_SECURITY_FORM_MOBILE_KEY = "mobile";
    /**
     * request中必须含有mobile参数
     */
    private String mobileParameter = SPRING_SECURITY_FORM_MOBILE_KEY;
    /**
     * post请求
     */
    private boolean postOnly = true;

    public SmsCodeAuthenticationFilter() {
        /**
         * 处理的手机验证码登录请求处理url
         */
        super(new AntPathRequestMatcher("/authentication/mobile", "POST"));
    }

    /**
     * 该方法走到最后就进入了 AuthenticationManager
     * @param request
     * @param response
     * @return
     * @throws AuthenticationException
     * @description
     *  AuthenticationManager 的作用是管理 AuthenticationProvider，
     *  找到 AuthenticationManager 的实现类 ProviderManager 的 authenticate() 方法
     */
    @Override
    public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException {
        //判断是不是post请求
        if (this.postOnly && !request.getMethod().equals("POST")) {
            throw new AuthenticationServiceException("Authentication method not supported: " + request.getMethod());
        } else {
            //从请求中获取手机号码
            String mobile = this.obtainMobile(request);
            if (mobile == null) {
                mobile = "";
            }
            mobile = mobile.trim();
            //创建 SmsCodeAuthenticationToken (未认证)
            SmsCodeAuthenticationToken authRequest = new SmsCodeAuthenticationToken(mobile);
            //设置用户信息
            this.setDetails(request, authRequest);
            /*
            * 但，这里有个问题，原文的意思是：
            * 可以看到里面的 AuthenticationProvider 类型的，while() 循环的原因是认证登录的方式多种多样，
            * 通过循环判断 AuthenticationProvider 的对象是否支持当前的验证类型。
            * 因此 AuthenticationManager 就需要把所有的 AuthenticationProvider 类型的对象收集起来。
            * 但！此处没有 while()，详情通过 .authenticate 进入 AuthenticationManager 类，往下找 ProviderManager 类
            * 返回 Authentication 实例*/
            return this.getAuthenticationManager().authenticate(authRequest);
        }
    }

    /**
     * 获取手机号
     */
    protected String obtainMobile(HttpServletRequest request) {
        return request.getParameter(this.mobileParameter);
    }

    /**
     * 设置用户信息
     * @param request
     * @param authRequest
     */
    protected void setDetails(HttpServletRequest request, SmsCodeAuthenticationToken authRequest) {
        authRequest.setDetails(this.authenticationDetailsSource.buildDetails(request));
    }

    public void setMobileParameter(String mobileParameter) {
        Assert.hasText(mobileParameter, "Username parameter must not be empty or null");
        this.mobileParameter = mobileParameter;
    }


    public void setPostOnly(boolean postOnly) {
        this.postOnly = postOnly;
    }

    public final String getMobileParameter() {
        return this.mobileParameter;
    }
}
